﻿/* UTF-8 */

/*
 * glyph.c: 此文件负责字符图形的生成，后端是Windows Typography或FreeType
 * Copyright 2016 tianxianglong. All rights reserved.
 */

/*
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef UNICODE
#define UNICODE
#endif

#ifndef _UNICODE
#define _UNICODE
#endif

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif

#include <Windows.h>
#include <windowsx.h>
#include "glyph.h"

static const MAT2 I = {
	{ 0, 1 }, { 0, 0 },
	{ 0, 0 }, { 0, 1 }
};

FIXED FixedFromDouble(float d)
{
	long l;
	l = (long)(d * 65536L);
	return *(FIXED *)&l;
}

struct FontSlot
{
	FONTOBJECT Font;
};

static const UINT FontCount = 999;
struct FontSlot FontArray[1000];

FONTOBJECT * GetFont(UINT ID)
{
	if (ID < FontCount)
		return &FontArray[ID].Font;
	
	return NULL;
}


#include <stdio.h>

void LoadAllMetric(HDC hdc, FONTOBJECTPTR Font)
{
	DWORD cch = Font->glyphset->cGlyphsSupported;
	Font->metrics = malloc(sizeof(*Font->metrics) * cch);
	memset(Font->metrics, 0, sizeof(*Font->metrics) * cch);

	Font->cbbitmaps = malloc(sizeof(*Font->cbbitmaps) * cch);
	memset(Font->cbbitmaps, 0, sizeof(*Font->cbbitmaps) * cch);

	if (Font->metrics == NULL || Font->cbbitmaps == NULL)
		return;
	
	GLYPHMETRICS *metric = Font->metrics;
	DWORD *cbbitmap = Font->cbbitmaps;
	WCRANGE *range = &Font->glyphset->ranges[0];
	WCHAR *cp = &Font->codepoints[0];
	
	for (DWORD i = 0; i < Font->glyphset->cRanges; ++i, ++range)
	{
		WCHAR ch = range->wcLow;
		for (USHORT j = 0; j < range->cGlyphs; ++j)
		{
			*cp = ch;
			Font->indices[ch] = (USHORT)(cp - &Font->codepoints[0]);
			*cbbitmap = GetGlyphOutlineW(
				hdc,
				ch,
				Font->Format,
				metric,
				0,
				NULL,
				&I
			);

			++cp;
			++ch;
			++metric;
			++cbbitmap;
		}
	}

	Font->Count = (UINT)(cp - &Font->codepoints[0]);

	return;
}

HFONT SelectGdiFont(HDC hdc, LPCWSTR FontName, UINT FontSize)
{
	HFONT FontInstance = NULL;
	FontInstance = CreateFontW(
		FontSize,
		0,
		0,
		0,
		FW_NORMAL,
		FALSE,
		FALSE,
		FALSE,
		DEFAULT_CHARSET,
		OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,
		CLEARTYPE_QUALITY,
		DEFAULT_PITCH,
		FontName
	);

	if (NULL == FontInstance)
		return NULL;

	return (HFONT)SelectObject(hdc, FontInstance);
}

void LoadAllBitmap(HDC hdc, FONTOBJECTPTR Font)
{
	GLYPHMETRICS *metric = Font->metrics;
	DWORD *cbbitmap = Font->cbbitmaps;
	

	Font->bitmaps = malloc(Font->glyphset->cGlyphsSupported * sizeof(*Font->bitmaps));

	LPBYTE *bitmap = Font->bitmaps;

	for (DWORD i = 0; i < Font->glyphset->cRanges; ++i)
	{
		WCRANGE glyphset = Font->glyphset->ranges[i];
		WCHAR ch = glyphset.wcLow;
		for (USHORT j = 0; j < glyphset.cGlyphs; ++j)
		{
			if (*cbbitmap > 0)
			{
				GLYPHMETRICS tmp;

				*bitmap = malloc(*cbbitmap);

				GetGlyphOutlineW(
					hdc,
					ch,
					Font->Format,
					&tmp,
					*cbbitmap,
					*bitmap,
					&I
				);

				if (tmp.gmBlackBoxX != metric->gmBlackBoxX ||
					tmp.gmBlackBoxY != metric->gmBlackBoxY ||
					tmp.gmCellIncX != metric->gmCellIncX ||
					tmp.gmCellIncY != metric->gmCellIncY)
				{
					DebugBreak();
				}
			}
			else
			{
				*bitmap = NULL;
			}

			++bitmap;
			++ch;
			++metric;
			++cbbitmap;
		}
	}
}

void LoadAllKerning(HDC hdc, FONTOBJECTPTR Font)
{
	DWORD Count = 0;
	Count = GetKerningPairsW(hdc, 0, NULL);

	if (Count > 0)
	{
		Font->kerning = malloc(Count * sizeof(KERNINGPAIR));
		if (Font->kerning != NULL)
			Font->kern_count = GetKerningPairsW(hdc, Count, Font->kerning);
	}
}

FONTOBJECTPTR MakeGdiFont(LPCWSTR FontName, UINT FontSize, UINT Format)
{
	FONTOBJECTPTR FontObject = NULL;
	FontObject = malloc(sizeof(*FontObject));

	if (FontObject == NULL)
		return NULL;

	memset(FontObject, 0, sizeof(*FontObject));

	HDC hdc = CreateCompatibleDC(NULL);
	if (hdc == NULL)
	{
		free(FontObject);
		return NULL;
	}

	HFONT oldfont = NULL;
	oldfont = SelectGdiFont(hdc, FontName, FontSize);

	FontObject->FontSize = FontSize;
	FontObject->Format = Format;

	DWORD cb;
	cb = GetFontUnicodeRanges(hdc, NULL);
	FontObject->glyphset = malloc(cb);
	GetFontUnicodeRanges(hdc, FontObject->glyphset);
	
	LoadAllMetric(hdc, FontObject);
	LoadAllBitmap(hdc, FontObject);
	LoadAllKerning(hdc, FontObject);

	oldfont = SelectFont(hdc, oldfont);
	DeleteFont(oldfont);
	DeleteDC(hdc);

	return FontObject;
}


DWORD CalcFontMemSize(FONTOBJECTPTR Font)
{
	DWORD ret = 0;

	ret += sizeof(Font);

	if (Font->glyphset)
		ret += Font->glyphset->cbThis;

	if (Font->metrics)
		ret += sizeof(*Font->metrics) * Font->glyphset->cGlyphsSupported;

	if (Font->cbbitmaps)
		ret += sizeof(*Font->cbbitmaps) * Font->glyphset->cGlyphsSupported;

	if (Font->bitmaps)
	{
		for (DWORD i = 0; i < Font->glyphset->cGlyphsSupported; ++i)
		{
			ret += Font->cbbitmaps[i];
		}
	}

	return ret;
}
